package com.jingdun.sport.authority

import com.jingdun.sport.bean.model.ApiResult
import com.jingdun.sport.bean.model.User
import com.jingdun.sport.common.util.PageParam
import com.jingdun.sport.data.environment.DataEnvironment
import com.jingdun.sport.data.environment.intGeneratedKey
import com.jingdun.sport.data.environment.page
import com.jingdun.sport.security.exception.UnauthorizedExceptionBase
import com.jingdun.sport.security.service.YamiUser
import com.jingdun.sport.security.util.SecurityUtils
import com.jingdun.sport.service.UserService
import com.losttemple.sql.language.dialects.DialectEnvironment
import com.losttemple.sql.language.operator.*
import com.losttemple.sql.language.reflection.allFields
import com.losttemple.sql.language.reflection.useAll
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Service
import org.springframework.web.bind.annotation.*
import java.net.URI

class UserRole {
    var user: String? = null
    var role: Int? = null
}

class Role {
    var name: String? = null
}

fun <T> DbSet<T>.page(current: Int?, size: Int?): DbSet<T> {
    val actualCurrent = 1
    val actualSize = 10
    return limit(actualSize)
}

@Service
class SecurityChecker {
    @Autowired
    private lateinit var sql: DialectEnvironment

    @Autowired
    private lateinit var userService: UserService

    val user: YamiUser
        get() {
            return SecurityUtils.getUser()
        }

    val authenticated: Boolean
        get() = SecurityUtils.getUser() != null

    val userEntity: User
        get() {
            val user = SecurityUtils.getUser()
            return userService.getUserByUserId(user.userId)
        }

    fun hasAuthority(authority: String): Boolean {
        val currentUser = SecurityUtils.getUser() ?: return false
        val userId = currentUser.userId
        return sql.run {
            fromUserAndAuthorities().where { (users eq userId) and (authorities.name eq authority) }
                    .aggregate { count { authorities.name } }()!! > 0
        }
    }
}

@RestController
@RequestMapping("/security")
class AuthorityController {
    @Autowired
    private lateinit var sql: DataEnvironment

    @Autowired
    private lateinit var userService: UserService

    @Autowired
    private lateinit var securityChecker: SecurityChecker

    @PostMapping("/users/{user}/roles")
    fun userAddRole(@PathVariable("user") user: String, role: Int?): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        try {
            if (!securityChecker.hasAuthority("Admin")) {
                //return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
            }
        }catch (e : UnauthorizedExceptionBase){
            apiResult.code = HttpStatus.UNAUTHORIZED.value()
            apiResult.msg = HttpStatus.UNAUTHORIZED.reasonPhrase
            return ResponseEntity.ok(apiResult)
        }
        if (role != null) {
            val hasRole = hasRole(role)
            if (!hasRole) {
                return ResponseEntity.ok(apiResult)
            }
            val userEntity = userService.getUserByUserId(user) ?: return ResponseEntity.ok(apiResult)
            sql.run {
                db { UserRoles(it) }.insert {
                    it.role(role)
                    it.user(user)
                }()
            }
            return ResponseEntity.ok(apiResult)
        }
	
        //return ResponseEntity.badRequest().body("role and user can't be null.")
	apiResult.code = HttpStatus.BAD_REQUEST.value()
        apiResult.msg = HttpStatus.BAD_REQUEST.reasonPhrase
        apiResult.data = "role and user can't be null."
        return ResponseEntity.ok(apiResult)
    }

    @GetMapping("/authorities")
    fun listAllAuthorities(page: PageParam<*>): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        try {
            if (!securityChecker.hasAuthority("Admin")) {
                //return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
            }
        }catch (e : UnauthorizedExceptionBase){
            apiResult.code = HttpStatus.UNAUTHORIZED.value()
            apiResult.msg = HttpStatus.UNAUTHORIZED.reasonPhrase
            return ResponseEntity.ok(apiResult)
        }
        
        val authorities = sql.run {
            from { Authorities(it) }
                    .page(page)
                    .with(allFields())
                    .select {
                        object {
                            val id = it.id()
                            val name = it.name()
                        }
                    }
        }
        apiResult.data = authorities
        return ResponseEntity.ok(apiResult)
    }

    @GetMapping("/roles")
    fun listAllRoles(page: PageParam<*>): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        try {
            if (!securityChecker.hasAuthority("Admin")) {
                //return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
            }
        }catch (e : UnauthorizedExceptionBase){
            apiResult.code = HttpStatus.UNAUTHORIZED.value()
            apiResult.msg = HttpStatus.UNAUTHORIZED.reasonPhrase
            return ResponseEntity.ok(apiResult)
        }
        val authorities = sql.run {
            from { Roles(it) }.page(page).with(allFields()).select {
                object {
                    val id = it.id()
                    val name = it.name()
                }
            }
        }
        apiResult.data = authorities
        return ResponseEntity.ok(apiResult)
    }

    @GetMapping("/roles/{role}/authorities")
    fun listAuthoritiesInRole(@PathVariable("role") role: Int): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        try {
            if (!securityChecker.hasAuthority("Admin")) {
                //return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
            }
        }catch (e : UnauthorizedExceptionBase){
            apiResult.code = HttpStatus.UNAUTHORIZED.value()
            apiResult.msg = HttpStatus.UNAUTHORIZED.reasonPhrase
            return ResponseEntity.ok(apiResult)
        }

        val authorities = sql.run {
            (from { RoleAuthorities(it) } innerJoin  from { Authorities(it) }).on { ras, authorities ->
                ras.authority eq authorities.id
            }.select { _, authorities ->
                authorities
            }.useAll().select {
                object {
                    val id = it.id()
                    val name = it.name()
                }
            }
        }
        
        apiResult.data = authorities
        return ResponseEntity.ok(apiResult)
    }

    @PostMapping("/roles/{role}/authorities")
    fun addAuthorityToRole(@PathVariable("role") role: Int, authority: Int?): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        try {
            if (!securityChecker.hasAuthority("Admin")) {
                //return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
            }
        }catch (e : UnauthorizedExceptionBase){
            apiResult.code = HttpStatus.UNAUTHORIZED.value()
            apiResult.msg = HttpStatus.UNAUTHORIZED.reasonPhrase
            return ResponseEntity.ok(apiResult)
        }

        if (authority == null) {
            apiResult.code = HttpStatus.BAD_REQUEST.value()
            apiResult.msg = HttpStatus.BAD_REQUEST.reasonPhrase
            apiResult.data = "authority needed."
            return ResponseEntity.ok(apiResult)
            //return ResponseEntity.badRequest().body("authority needed.")
        }

        val hasRole = hasRole(role)
        if (!hasRole) {
            //return ResponseEntity.notFound().build()
            return ResponseEntity.ok(apiResult)
        }
        val hasAuthority = sql.run {
            from { Authorities(it) }.where { id eq authority }.aggregate { count { id } }()!! > 0
        }
        if (!hasAuthority) {
            //return ResponseEntity.notFound().build()
            return ResponseEntity.ok(apiResult)
        }
        sql.run {
            db { RoleAuthorities(it) }.insert {
                it.role(role)
                it.authority(authority)
            }()
        }
        //return ResponseEntity.ok().build()
        return ResponseEntity.ok(apiResult)
    }

    @PostMapping("/roles")
    fun createRole(@RequestBody role: Role): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        try {
            if (!securityChecker.hasAuthority("Admin")) {
                //return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
            }
        }catch (e : UnauthorizedExceptionBase){
            apiResult.code = HttpStatus.UNAUTHORIZED.value()
            apiResult.msg = HttpStatus.UNAUTHORIZED.reasonPhrase
            return ResponseEntity.ok(apiResult)
        }
        var roleId: Int? = 0
        sql.run {
            db { Roles(it) }.insert {
                it.name(role.name)
            }.ret {
                roleId = intGeneratedKey()
            }()
        }

        //新增之后跳转到查看角色详情/security/roles/${roleId}
        //return ResponseEntity.created(URI("/security/roles/${roleId}")).build()

        apiResult.data = "/security/roles/${roleId}"
        return ResponseEntity.ok(apiResult)
    }

    @GetMapping("/users")
    fun listUsers(page: PageParam<User>): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        val users = userService.page(page)
        apiResult.data = users
        return ResponseEntity.ok(apiResult)
    }

    private fun hasRole(role: Int): Boolean {
        return sql.run {
            from { Roles(it) }.where { id eq role }.aggregate { count { id } }()!! > 0
        }
    }
}